package com.gitee.wsl.doc.prosemirror.plugin.writer

import com.gitee.wsl.doc.prosemirror.model.mark.Mark
import com.gitee.wsl.doc.prosemirror.model.node.Node

/**
 * 节点序列化函数类型
 */
typealias NodeSerializer = (state: MarkdownSerializerState, node: Node, parent: Node, index: Int) -> Unit


/**
 * Markdown序列化选项
 */
data class MarkdownSerializerOptions(
    var tightLists: Boolean = false,
    var escapeExtraCharacters: Regex? = null,
    var hardBreakNodeName: String = "hard_break",
    var strict: Boolean = false
)

/**
 * 完整的Markdown序列化器
 */
class MarkdownSerializer(
    val nodes: Map<String, NodeSerializer>,
    val marks: Map<String, MarkSerializerSpec>,
    val options: MarkdownSerializerOptions = MarkdownSerializerOptions()
) {

    /**
     * 将节点序列化为Markdown字符串
     */
    fun serialize(content: Node): String {
        val state = MarkdownSerializerState(nodes, marks, options)
        state.renderContent(content)
        return state.out.trim()
    }

    companion object {

    }
}

/**
 * 默认Markdown序列化器实现
 */
object DefaultMarkdownSerializer {
    val nodes = mapOf<String, NodeSerializer>(
        "blockquote" to { state, node, _, _ ->
            state.wrapBlock("> ", null, node) {
                state.renderContent(node)
            }
        },
        "code_block" to { state, node, _, _ ->
            val backticks = Regex("`{3,}").findAll(node.textContent).toList()
            val fence = if (backticks.isNotEmpty()) {
                backticks.maxByOrNull { it.value.length }!!.value + "`"
            } else {
                "```"
            }

            state.write("$fence${node.attrs["params"] ?: ""}\n")
            state.text(node.textContent, false)
            state.write("\n")
            state.write(fence)
            state.closeBlock(node)
        },
        "heading" to { state, node, _, _ ->
            val level = node.attrs["level"] as? Int ?: 1
            state.write("${"#".repeat(level)} ")
            state.renderInline(node, false)
            state.closeBlock(node)
        },
        "horizontal_rule" to { state, node, _, _ ->
            val markup = node.attrs["markup"] as? String ?: "---"
            state.write(markup)
            state.closeBlock(node)
        },
        "bullet_list" to { state, node, _, _ ->
            val bullet = node.attrs["bullet"] as? String ?: "*"
            state.renderList(node, "  ") { "$bullet " }
        },
        "ordered_list" to { state, node, _, _ ->
            val start = node.attrs["order"] as? Int ?: 1
            val maxW = (start + node.childCount - 1).toString().length
            val space = " ".repeat(maxW + 2)
            state.renderList(node, space) { i ->
                val nStr = (start + i).toString()
                " ".repeat( maxW - nStr.length) + "$nStr. "
            }
        },
            "list_item" to { state, node, _, _ ->
                state.renderContent(node)
            },
            "paragraph" to { state, node, _, _ ->
                state.renderInline(node)
                state.closeBlock(node)
            },
            "image" to { state, node, _, _ ->
                val alt = node.attrs["alt"] as? String ?: ""
                val src = node.attrs["src"] as? String ?: ""
                val title = node.attrs["title"] as? String
                val escapedSrc = src.replace(Regex("[()]"), "\\\\$0")
                val titlePart = if (title != null) " \"${title.replace("\"", "\\\"")}\"" else ""
                state.write("![${state.esc(alt)}]($escapedSrc$titlePart)")
            },
            "hard_break" to { state, node, parent, index ->
                for (i in index + 1 until parent.childCount) {
                    if (parent.child(i).type != node.type) {
                        state.write("\\\n")
                        return@to
                    }
                }
            },
            "text" to { state, node, _, _ ->
                state.text(node.text ?: "", state.inAutolink != true)
            }
            )

    val marks = mapOf<String, MarkSerializerSpec>(
                "em" to MarkSerializerSpec(
                    open = "*",
                    close = "*",
                    mixable = true,
                    expelEnclosingWhitespace = true
                ),
                "strong" to MarkSerializerSpec(
                    open = "**",
                    close = "**",
                    mixable = true,
                    expelEnclosingWhitespace = true
                ),
                "link" to FunctionalMarkSerializerSpec(
                    openFunc = { state, mark, parent, index ->
                        state.inAutolink = isPlainURL(mark, parent, index)
                        if (state.inAutolink == true) "<" else "["
                    },
                    closeFunc = { state, mark, parent, index ->
                        val inAutolink = state.inAutolink
                        state.inAutolink = null
                        if (inAutolink == true) {
                            ">"
                        } else {
                            val href = mark.attrs["href"] as? String ?: ""
                            val title = mark.attrs["title"] as? String
                            val escapedHref = href.replace(Regex("[()\"]"), "\\\\$0")
                            val titlePart = if (title != null) " \"${title.replace("\"", "\\\"")}\"" else ""
                            "]($escapedHref$titlePart)"
                        }
                    },
                    mixable = true
                ),
                "code" to FunctionalMarkSerializerSpec(
                    openFunc = { _, _, parent, index -> backticksFor(parent.child(index), -1) },
                    closeFunc = { _, _, parent, index -> backticksFor(parent.child(index - 1), 1) },
                    escape = false
                )
            )

            /**
             * 计算反引号数量
             */
    private fun backticksFor(node: Node, side: Int): String {
        var len = 0
        if (node.isText) {
            val ticks = Regex("`+").findAll(node.text ?: "")
            len = ticks.maxOfOrNull { it.value.length } ?: 0
        }

        var result = if (len > 0 && side > 0) " `" else "`"
        for (i in 0 until len) {
            result += "`"
        }
        if (len > 0 && side < 0) result += " "
        return result
    }

            /**
             * 检查是否为纯URL链接
             */
    private fun isPlainURL(link: Mark, parent: Node, index: Int): Boolean {
        if (link.attrs.containsKey("title") && link.attrs["title"] != null) {
            return false
        }
        val href = link.attrs["href"] as? String ?: return false
        if (!Regex("^\\w+:").containsMatchIn(href)) return false

        val content = parent.child(index)
        if (!content.isText || content.text != href || content.marks.lastOrNull() != link) return false

        return index == parent.childCount - 1 || !link.isInSet(parent.child(index + 1).marks)

    }

     /**
      * 创建默认序列化器
      */
     fun create(): MarkdownSerializer {
         return MarkdownSerializer(nodes, marks)
     }
}